package com.terrynoya.fight.stateMachine
{
	import com.terrynoya.coreLib.MHashMap;
	import com.terrynoya.fight.combat.EngineObject;
	import com.terrynoya.fight.combat.FightEngine;
	import com.terrynoya.fight.emuns.MoveType;
	import com.terrynoya.fight.emuns.PhysicsEnum;
	import com.terrynoya.fight.emuns.StateTypeEnum;
	import com.terrynoya.fight.evaluation.Expression;
	import com.terrynoya.fight.evaluation.Node;
	import com.terrynoya.fight.io.KeyValuePair;
	import com.terrynoya.fight.io.TextFile;
	import com.terrynoya.fight.io.TextSection;
	import com.terrynoya.fight.stateMachine.controllers.ChangeAni;
	import com.terrynoya.fight.stateMachine.controllers.ChangeAnim2;
	import com.terrynoya.fight.stateMachine.controllers.ChangeState;
	import com.terrynoya.fight.stateMachine.controllers.CtrlSet;
	import com.terrynoya.fight.stateMachine.controllers.DefenceMulSet;
	import com.terrynoya.fight.stateMachine.controllers.DestroySelf;
	import com.terrynoya.fight.stateMachine.controllers.ExplodCtrl;
	import com.terrynoya.fight.stateMachine.controllers.FallEnvShake;
	import com.terrynoya.fight.stateMachine.controllers.ForceFeedback;
	import com.terrynoya.fight.stateMachine.controllers.HelperCtrl;
	import com.terrynoya.fight.stateMachine.controllers.HitDef;
	import com.terrynoya.fight.stateMachine.controllers.HitFallVel;
	import com.terrynoya.fight.stateMachine.controllers.HitVelSet;
	import com.terrynoya.fight.stateMachine.controllers.PosAdd;
	import com.terrynoya.fight.stateMachine.controllers.PosSet;
	import com.terrynoya.fight.stateMachine.controllers.ProjectileCtrl;
	import com.terrynoya.fight.stateMachine.controllers.SelfState;
	import com.terrynoya.fight.stateMachine.controllers.StateTypeSet;
	import com.terrynoya.fight.stateMachine.controllers.TargetBind;
	import com.terrynoya.fight.stateMachine.controllers.TargetFacing;
	import com.terrynoya.fight.stateMachine.controllers.TargetState;
	import com.terrynoya.fight.stateMachine.controllers.Turn;
	import com.terrynoya.fight.stateMachine.controllers.VarAdd;
	import com.terrynoya.fight.stateMachine.controllers.VarSet;
	import com.terrynoya.fight.stateMachine.controllers.VelAdd;
	import com.terrynoya.fight.stateMachine.controllers.VelMul;
	import com.terrynoya.fight.stateMachine.controllers.VelSet;
	import com.terrynoya.fight.stateMachine.controllers.attrParser.StateCtrlAttrBuilder;
	import com.terrynoya.fight.stateMachine.triggers.SelfAnimExist;
	import com.terrynoya.fight.stateMachine.triggers.StateType;
	import com.terrynoya.fight.util.Misc;

	public class StateSystem extends EngineObject
	{
		public static const STATE_DEF_REG:RegExp = /Statedef\s*(-?\d+).*/;
		public static const STATE_CTRL_REG:RegExp = /^State\s+(\S.*)$/;
		
		private var _ctrlClassMap:MHashMap;
		
		private var _builder:StateCtrlAttrBuilder;
		
		public function StateSystem(engine:FightEngine)
		{
			super(engine);
			this._builder = new StateCtrlAttrBuilder(engine);
			this._ctrlClassMap = new MHashMap();
			this._ctrlClassMap.put("ChangeState",ChangeState);
			this._ctrlClassMap.put("ChangeAnim",ChangeAni);
			this._ctrlClassMap.put("ChangeAnim2",ChangeAnim2);
			this._ctrlClassMap.put("CtrlSet",CtrlSet);
			this._ctrlClassMap.put("VelSet",VelSet);
			this._ctrlClassMap.put("HitDef",HitDef);
			this._ctrlClassMap.put("PosSet",PosSet);
			this._ctrlClassMap.put("VelSet",VelSet);
			this._ctrlClassMap.put("VarSet",VarSet);
			this._ctrlClassMap.put("VarAdd",VarAdd);
			this._ctrlClassMap.put("PosAdd",PosAdd);
			this._ctrlClassMap.put("VelMul",VelMul);
			this._ctrlClassMap.put("VelAdd",VelAdd);
			this._ctrlClassMap.put("StateTypeSet",StateTypeSet);
			this._ctrlClassMap.put("ForceFeedback",ForceFeedback);
			this._ctrlClassMap.put("HitVelSet",HitVelSet);
			this._ctrlClassMap.put("DefenceMulSet",DefenceMulSet);
			this._ctrlClassMap.put("FallEnvShake",FallEnvShake);
			this._ctrlClassMap.put("HitFallVel",HitFallVel);
			this._ctrlClassMap.put("TargetBind",TargetBind);
			this._ctrlClassMap.put("TargetState",TargetState);
			this._ctrlClassMap.put("SelfState",SelfState);
			this._ctrlClassMap.put("TargetFacing",TargetFacing);
			this._ctrlClassMap.put("Turn",Turn);
			this._ctrlClassMap.put("Projectile",ProjectileCtrl);
			this._ctrlClassMap.put("Explod",ExplodCtrl);
			this._ctrlClassMap.put("Helper",HelperCtrl);
			this._ctrlClassMap.put("DestroySelf",DestroySelf);
		}
		
		public function getStates(textFile:TextFile):MHashMap
		{
			var rlt:MHashMap = new MHashMap();
			var offset:int = 0;
			var state:State;
			var ctrls:Array;
			while(offset < textFile.sections.length)
			{
				var section:TextSection = textFile.sections[offset];
				offset ++;
				if(STATE_DEF_REG.test(section.title))
				{
					var stateId:int = this.getStateId(section);
					if(rlt.containsKey(stateId))
					{
						state = rlt.getValue(stateId);
						this.initStateProperty(state,section);
					}
					else
					{
						state = this.createState(section);
						rlt.put(state.id,state);
					}
				}
				else if(STATE_CTRL_REG.test(section.title))
				{
					var ctrl:StateController = this.createController(section);
					if(ctrl == null)
					{
						trace("ctrl not exist:",section.getValue("type"));
					}
					else
					{
						state.addController(ctrl);
					}
				}
			}
			return rlt;
		}
		
		private function createController(textSection:TextSection):StateController
		{
			var title:String = textSection.title;
			var matches:Array = title.match(STATE_CTRL_REG);
			var stateName:String = matches[1];
			var type:String = textSection.getValue("type");
			var ctrl:StateController = this.getStateCtrl(type,stateName);
			this._builder.build(ctrl,textSection);
			this.buildTriggers(textSection,ctrl);
			return ctrl;
		}
		
		public function buildExpression(input:String):Node
		{
			return this.engine.evalSystem.createExpression(input);
		}
		
		public function buildFullExpression(input:String):Array
		{
			return this.engine.evalSystem.createFullExpression(input);
		}
		
		public function buildTriggers(textSection:TextSection,ctrl:StateController):void
		{
			if(ctrl == null)
			{
				return;
			}
			var parsedLines:Array = textSection.parsedLines;
			for (var i:int = 0; i < parsedLines.length; i++) 
			{
				var kv:KeyValuePair = parsedLines[i];
				var key:String = kv.key.toLowerCase();
				var triggerStr:String = "trigger";
				var triggerIndex:int = key.indexOf(triggerStr);
				if(triggerIndex != -1)
				{
					var node:Node = this.engine.evalSystem.createExpression(kv.value);
					var id:int = 0;
					if(key == "triggerall")
					{
						id = 0;
					}
					else
					{
						id = int(key.substr(triggerStr.length));
					}
					if(node != null)
					{
						ctrl.addTrigger(id,new Expression(node,kv.value));	
					}
				}
			}
		}
		
		private function getStateCtrl(type:String,stateName:String):StateController
		{
			if(!this._ctrlClassMap.containsKey(type))
			{
				return null;
			}
			var ctrlClass:Class = this._ctrlClassMap.getValue(type);
			var rlt:StateController = new ctrlClass(stateName);
			rlt.stateSystem = this;
			return rlt;
		}
		
		private function createState(textSection:TextSection):State
		{
			var stateId:int = this.getStateId(textSection);
			var state:State = new State(stateId);
			this.initStateProperty(state,textSection);
			return state;
		}
		
		private function getStateId(textSection:TextSection):int
		{
			var title:String = textSection.title;
			var matches:Array = title.match(STATE_DEF_REG);
			var stateId:int = matches[1];
			return stateId;
		}
		
		private function initStateProperty(state:State,textSection:TextSection):void
		{
			for (var i:int = 0; i < textSection.parsedLines.length; i++) 
			{
				var vo:KeyValuePair = textSection.parsedLines[i];
				switch(vo.key)
				{
					case "type":
					{
						state.type = Misc.getStateType(vo.value);
						break;
					}
					case "movetype":
					{
						state.moveType = Misc.getMoveType(vo.value);
						break;
					}
					case "physics":
					{
						state.physics = Misc.getPhysicsType(vo.value);
						break;
					}
					case "anim":
					{
						state.animateId = int(vo.value);
						break;
					}
					case "ctrl":
					{
						state.playerControl = int(vo.value);
						break;
					}
					case "velset":
					{
						state.velSet = Misc.getVelocity(vo.value);
						break;
					}
				}
			}
		}
	}
}